using System;
using System.Collections.Generic;
using System.Linq;
using Framework.Core.CommandLine;
using Framework.Core.Io;
using log4net;

namespace Framework.Core.BootStrapper
{
    public class BootStrapper : IBootStrapper
    {
        private readonly IList<Type> availablePlugins = new List<Type>();
        private readonly ICommandLineParser commandLineParser;
        private readonly IEnvironmentAdapter environmentAdapter;
        private static readonly ILog Logger = LogManager.GetLogger(typeof(BootStrapper));

        public BootStrapper()
            : this(new CommandLineParser(), new EnvironmentAdapter())
        {
        }

        internal BootStrapper(ICommandLineParser commandLineParser, IEnvironmentAdapter environmentAdapter)
        {
            this.commandLineParser = commandLineParser;
            this.environmentAdapter = environmentAdapter;
        }

        public IBootStrapper Add<TPlugin>()
            where TPlugin : IPlugin
        {
            availablePlugins.Add(typeof(TPlugin));
            return this;
        }

        public void Start(string[] args)
        {
            PluginCommandLineArgs pluginArgs;

            try
            {
                Logger.InfoFormat("Command Line ({0}) args are : {1}", args.Count(), string.Join(" ", args));
                pluginArgs = commandLineParser.Parse<PluginCommandLineArgs>(args);
            }
            catch (ArgumentException)
            {
                Logger.ErrorFormat("missing /p selector args");
                environmentAdapter.Exit(-1);
                return;
            }

            var candidatePluginType = availablePlugins.FirstOrDefault(p => p.Name == pluginArgs.Plugin);

            if (candidatePluginType != null)
            {
                var plugin = ((IPlugin)Activator.CreateInstance(candidatePluginType));

                try
                {
                    environmentAdapter.Exit(plugin.Execute(args));
                }
                catch (Exception e)
                {
                    const string failedToRunPlugin = "Failed to run plugin: {0}";
                    Logger.ErrorFormat(failedToRunPlugin, e.Message);
                    Logger.DebugFormat(failedToRunPlugin, e);

                    environmentAdapter.Exit(-1);
                }
            }
            else
            {
                Logger.ErrorFormat("plugin {0} does not exists", pluginArgs.Plugin);
                environmentAdapter.Exit(-1);
            }
        }
    }
}